const hasProto = '__proto__' in {}
const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
const seenObjects = new Set()

let uid = 0
class Observer {
  constructor(value) {
    this.data = value
    this.dep = new Dep()

    // 给value 设置一个__ob__ , 表明已经被观察过,是响应式数据
    def(value, '__ob__', this)

    if (Array.isArray(value)) {
      const augment = hasProto ? protoAugment : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  walk (obj) {
    console.log(22222222)
    const keys = Object.keys(obj)
    for (const key of keys) {
      defineReactive(obj, key, obj[key])
    }
  }
  observeArray (items) {
    for (let i = 0; i < items.length; i++) {
      const key = items[i];
      observer(key)
    }
  }
}

class Dep {
  constructor() {
    this.id = uid++
    this.subs = []
  }

  addSub (sub) {
    this.subs.push(sub)
  }

  removeSub (sub) {
    remove(this.subs, sub)
  }

  depend () {
    if (window.target) {
      window.target.addDep(this)
    }
  }

  notify () {
    const subs = this.subs.slice()
    for (const key of subs) {
      key.update()
    }
  }
}

class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm
    // 标记订阅过的dep, 防止重复订阅
    this.deps = []
    this.depIds = new Set()

    if (options) {
      this.deep = !!options.deep
    } else {
      this.deep = false
    }
    this.getter = parsePath(expOrFn)
    this.cb = cb
    console.log('get1')
    this.val = this.get()

  }

  addDep (dep) {
    const id = dep.id
    if (!this.depIds.has(id)) {
      this.depIds.add(id)
      this.deps.push(dep)
      dep.addSub(this)
    }
  }

  get () {
    window.target = this
    let value = this.getter.call(this.vm, this.vm.data)
    if (this.deep) {
      debugger

      traverse(value)
    }
    window.target = undefined
    return value
  }

  update () {
    const oldValue = this.value
    console.log('get2')
    this.value = this.get()
    this.cb.call(this.vm, this.value, oldValue)
    console.log('updated')
  }
}

// data.a.b  分割成 [data,a,b], 去拿{data: {a:{b: 1}}}  去拿b的数据
function parsePath (path) {
  if (/[^\w.$]/.test(path)) {
    return
  }

  const segments = path.split('.')
  return function (obj) {
    for (const key of segments) {
      if (!obj) return
      obj = obj[key]
    }
    return obj
  }
}

function remove (arr, item) {
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}



;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
  // 缓存原生方法
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break;
      case 'splice':
        inserted = args.slice(2)
      default:
        break;
    }
    if (inserted) ob.observeArray(inserted)
    ob.dep.notify()
    return result
  })

})




function defineReactive (data, key, val) {
  // 如果val 是对象, 将val 也设置成为响应式数据
  let childOb = observer(val)
  console.log(childOb, 1)
  let dep = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function () {
      dep.depend()
      if (childOb) {
        childOb.dep.depend()
      }
      return val
    },
    set: function (newVal) {
      if (val === newVal) {
        return
      }
      dep.notify()
      console.log('set')
      val = newVal
    }
  })
}


function observer (value, asRootData) {
  if (!isObject(value)) {
    return
  }
  let ob
  if (Object.hasOwnProperty(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else {
    ob = new Observer(value)
  }
  return ob
}

// 设置一个属性
function def (obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    configurable: true,
    writable: true
  })
}

function copyAugment (target, src, keys) {
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    def(target, key, src[key])
  }
}

function protoAugment (target, src, keys) {
  target.__proto__ = src
}

function isObject (val) {
  return val != null && typeof val === 'object'
};

function traverse (val) {
  _traverse(val.seenObjects)
  // 每次递归seenObject 都是空的
  seenObjects.clear()
}

function _traverse (val, seen) {
  let i, keys
  // 不是数组或者object, 直接返回
  const isA = Array.isArray(val)
  if ((!isA && !isObject(val)) || Object.isFrozen(val)) {
    return
  }

  // 如果是响应式数据, 记录depId
  if (val.__ob__) {
    const depId = val.__ob__.dep.id
    if (seen.has(depId)) {
      return
    }
    seen.add(depId)
  }

  if (isA) {
    i = val.length
    while (i--) _traverse(val[i], seen)
  } else {
    keys = Object.keys(val)
    i = keys.length
    while (i--) _traverse(val[keys[i]], seen)
  }

}